#include <httpserver.h>

#define _BSD_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <netdb.h>
#include <dirent.h>
#include <signal.h>

#include <transfert-client.h>
#include <config-client.h>
#include <lock.h>
#include <log.h>

char* htmlContent;
int htmlContentSize = SIZE_STEP;

char baseName[128];

void SendResponse(int sd, int ok){
	char header[512]="";
	char buffer[128];
	if(ok==1){
		sprintf(buffer, "HTTP/1.0 200 OK\r\n");
		strcat(header, buffer);
		sprintf(buffer, "Content-Length: %d\r\n", strlen(htmlContent));
		strcat(header, buffer);
		sprintf(buffer, "Content-Type: text/html\r\n");
		strcat(header, buffer);
	}else{
		sprintf(header,"HTTP/1.0 401 Authorization Required\r\nWWW-Authenticate: Basic realm=\"qube-monitor\"\nContent-Length: 26\r\n\r\n<html>401 Access Forbidden</html>\n\n");
		write(sd, header, strlen(header));
		free(htmlContent);
		return;		
	}
	strcat(header, "\r\n");
	write(sd, header, strlen(header));
	
	write(sd, htmlContent, strlen(htmlContent));
	free(htmlContent);
}

int MakeResponse(int sd, char* sName, int nReport){
	htmlContent=(char*)malloc(SIZE_STEP);
	htmlContent[0]='\000';
	
	
		
	if(sName[0]=='\000'){
		/*
		 * Page d'accueil
		 */
		
		HtmlAppend("<html>\n<head>\n<title>qube-monitor</title>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"");
		HtmlAppend(baseName);
		HtmlAppend("qube.css\"/>\n</head>\n<body>\n<h1>qube<sup>monitor</sup><span id=\"about\"><a href=\"");
		HtmlAppend(baseName);
		HtmlAppend(".about\">?</a></span></h1>\n");
		HtmlAppend(MakeNavBar(sName, nReport));
		struct dirent **dirlist;
		int nbr=scandir(CLIENT_DATABASE, &dirlist, NULL, alphasort);
		if(nbr==-1){
			return 0;
		}
		int i;
		HtmlAppend("<p>Liste des serveurs monitor&eacute;s :</p><ul>\n");
		for(i=0; i<nbr; ++i){
			int j=-1;
			while(ServerName(++j)!=NULL && strcmp(ServerName(j),dirlist[i]->d_name)!=0);
			if(ServerName(j)!=NULL){		
				HtmlAppend("\t<li>");
				HtmlAppend("<a href=\"");
				HtmlAppend(dirlist[i]->d_name);
				HtmlAppend("\">");
				HtmlAppend(dirlist[i]->d_name);
				HtmlAppend("</a></li>\n");
			}
			free(dirlist[i]);
		}
		free(dirlist);
		HtmlAppend("</ul>");
		HtmlAppend("<p>Effectuer un <a href=\".test\">.test</a> de connexion avec les serveurs</p>");
		
	}else if(strcmp(sName, "qube.css")==0){
		/*
		 * Lecture du fichier CSS
		 */
	
		int fd = open(CLIENT_CSS, O_RDONLY);
		char buf[256];
		int nbr;
		while((nbr=read(fd,buf,255))>0){
			buf[nbr]='\00';
			HtmlAppend(buf);
		}
	}else if(strcmp(sName, ".about")==0){
		HtmlAppend("<html>\n<head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n<title>qube-monitor</title>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"");
		HtmlAppend(baseName);
		HtmlAppend("qube.css\"/>\n</head>\n<body>\n<h1>qube<sup>monitor</sup><span id=\"about\"><a href=\"");
		HtmlAppend(baseName);
		HtmlAppend(".about\">?</a></span></h1>\n");
		HtmlAppend(MakeNavBar(".about", -1));
		HtmlAppend("<p>L'&Eacute;quipe du projet qube-monitor :<p>\n");
		HtmlAppend("<ul>\n");
		HtmlAppend("\t<li>Nicolas Szabo (CdP)</li>\n");
		HtmlAppend("\t<li>Arnaud Muller</li>\n");
		HtmlAppend("\t<li>William Pastor</li>\n");
		HtmlAppend("\t<li>Ga&euml;tan Pinna</li>\n");
		HtmlAppend("</ul>\n");
		
	}else if(strcmp(sName, ".test")==0){
		int nbr=TestAllServers();
		
		HtmlAppend("<html>\n<head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n<title>qube-monitor</title>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"");
		HtmlAppend(baseName);
		HtmlAppend("qube.css\"/>\n</head>\n<body>\n<h1>qube<sup>monitor</sup><span id=\"about\"><a href=\"");
		HtmlAppend(baseName);
		HtmlAppend(".about\">?</a></span></h1>\n");

		if(FileExists(CLIENT_TESTS_RESULTS) && nbr==1){
		HtmlAppend(MakeNavBar(".test", -1));
		HtmlAppend("<p>R&eacute;sultats des tests de connexion :</p>");
			char buffer[100+1];
			HtmlAppend("<textarea readonly cols=100 rows=28>");
			
			int fd=open(CLIENT_TESTS_RESULTS, O_RDONLY);
			int ret;
			while((ret=read(fd, buffer, sizeof(buffer)-1))){
				buffer[ret]='\000';
				HtmlAppend(buffer);
			}
			close(fd);
			HtmlAppend("</textarea>");
			
		}else{
			HtmlAppend(MakeNavBar(".test", -1));
			HtmlAppend("<p>La proc&eacute;dure de test n'a pas fonctionn&eacute;.</p>");
		}
		remove(CLIENT_TESTS_RESULTS);
	}else{
		/* 
		 * Lecture d'un rapport
		 */
		HtmlAppend("<html>\n<head>\n<meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n<title>qube-monitor</title>\n<link rel=\"stylesheet\" type=\"text/css\" href=\"");
		HtmlAppend(baseName);
		HtmlAppend("qube.css\"/>\n</head>\n<body>\n<h1>qube<sup>monitor</sup><span id=\"about\"><a href=\"");
		HtmlAppend(baseName);
		HtmlAppend(".about\">?</a></span></h1>\n");
		if(nReport>=0){
			char fileName[128];
			strcpy(fileName, GetFileName(sName, nReport));
			if(FileExists(fileName)){
			HtmlAppend(MakeNavBar(sName, nReport));
				char buffer[100+1];
				HtmlAppend("<textarea readonly cols=100 rows=28>");
				
				int fd=open(fileName, O_RDONLY);
				int ret;
				while((ret=read(fd, buffer, sizeof(buffer)-1))){
					buffer[ret]='\000';
					HtmlAppend(buffer);
				}
				close(fd);
				HtmlAppend("</textarea>");
				
			}else{
				HtmlAppend(MakeNavBar("", -1));
				HtmlAppend("<p>La page demand&eacute;e n'existe pas.</p>");
			}
		}else{
			/*
			 * Liste des rapports disponible
			 */
			HtmlAppend(MakeNavBar(sName, nReport));
			char sdir[256] = CLIENT_DATABASE;
			strcat(sdir, sName); struct dirent **dirlist;
			
			int nbr = scandir(sdir, &dirlist, NULL, alphasort);
			if(nbr>2){
				int i;
				HtmlAppend("<p>Liste des rapports pour le serveur ");
				HtmlAppend(sName);
				HtmlAppend(" :</p>\n<p id=\"rlist\">");
				for(i=0; i<nbr; ++i){
					int nbreport;
					if(sscanf(dirlist[i]->d_name, "report%d", &nbreport)==1){
						char reportlink[128], reportnbchar[8];;
						sprintf(reportlink, "report%.4d", nbreport);
						sprintf(reportnbchar, "%d", nbreport);
						HtmlAppend("<a href=\"");
						HtmlAppend(baseName);
						HtmlAppend(sName);
						HtmlAppend("/");
						HtmlAppend(reportnbchar);
						HtmlAppend("\">");
						HtmlAppend(reportlink);
						HtmlAppend("</a>, ");
					}
					
					free(dirlist[i]);
				}
				htmlContent[strlen(htmlContent)-2]='\00';
				HtmlAppend("</p>");
				free(dirlist);
			}else{
				HtmlAppend("<p>Aucun rapport n'est disponible pour ce serveur<p>");
			}
			
		}
	}
	
	return 1;
}

int ReadHeader(char* header, char* sName, int* nReport){
	char path[512];
	sscanf(header, "GET %[^ ] HTTP%*s", path);
	
	char cnReport[5];
	sName[0]='\000';
	sscanf(path, "/%[^/\r]/%[^/]", sName, cnReport);
	
	if(isdigit(cnReport[0])){
		*nReport=atoi(cnReport);
	}else{
		*nReport=-1;
	}

	char* cLine=header;
	char auth[128]="";
	while((cLine=GetLine(cLine))!=NULL){
		sscanf(cLine, "Host: %[^\r]", baseName);
		sscanf(cLine, "Authorization: Basic %[^\r]", auth);
	}
		
	char cbaseName[128];
	strcpy(cbaseName, baseName);
	strcpy(baseName, "http://");
	strcat(baseName, cbaseName);
	strcat(baseName, "/");
	
	if(ConfHttpPass()!=NULL && strcmp(auth, ConfHttpPass())!=0){
		return 0;
	}
	return 1;
}

void HttpRequest(int sd){
	char header[512]="";
	int ret;
	
		ret=read(sd, header, sizeof(header));
		if(ret==-1){
			LogError("Ne peut lire la requête http sur le socket", 1);
		}
		
		char sName[LEN_SERVERNAME+1];
		int nReport;
		ret =ReadHeader(header, sName, &nReport);
		if(ret==1){
			MakeResponse(sd, sName, nReport);
		}
		SendResponse(sd, ret);
		
		close(sd);
}

int HttpSocketInit(){
	int ret ,sd;	
	struct sockaddr_in serveur;
	sd=socket(AF_INET,SOCK_STREAM,0);

	serveur.sin_family=AF_INET;
	serveur.sin_addr.s_addr=htons(INADDR_ANY);
	serveur.sin_port=htons(ConfHttpPort());

	ret=bind(sd, (struct sockaddr *)&serveur, sizeof(serveur));
	if(ret<0){
		LogError("Serveur HTTP : Bind échoué",3);
		exit(0);
	}
	ret=listen(sd,10);
	
	return sd;
}

void child_handler(){
	wait();
}

void HttpListenLoop(int sd){
	struct sockaddr_in clientAddress;
	int clientSize=sizeof(clientAddress);
	
	char httpMess[128] = "Démarrage du serveur http, écoute sur port ";
	sprintf(httpMess+strlen(httpMess), "%d", ConfHttpPort());
	LogInfo(httpMess);
	
	siginterrupt(SIGCHLD, 0);
	signal(SIGCHLD, child_handler);

	for(;;){
		int nsd=accept(sd, (struct sockaddr_in *)&clientAddress, &clientSize);
		
		int pid=fork();
		switch(pid){
			case -1:{
				LogWarning("Traitement d'une nouvelle requête http impossible");
				break;
			}	
			case 0:{
				HttpRequest(nsd);
				exit(0);
			}
		}
	}
}

char* MakeNavBar(char* sName, int report){
	char ret[1024], buffer[128];
	sprintf(ret, "<div id=\"navbar\"><a href=\"%s\">qube</a>", baseName);
	if(sName[0]!='\000'){
		sprintf(buffer, " <b>></b> <a href=\"%s%s\">%s</a>", baseName, sName, sName);
		strcat(ret, buffer);
	}else{
		strcat(ret, "</div>\n");
		return ret;
	}
	if(report>=0){
		sprintf(buffer, " <b>></b> <a href=\"%s%s/%d\">report%.4d</a>", baseName, sName, report, report);
		strcat(ret, buffer);
		
		char next[128];
		strcpy(next, CLIENT_DATABASE);
		strcat(next, sName);
		strcat(next, "/");
		sprintf(buffer, "report%.4d", report+1);
		strcat(next, buffer);
		strcat(ret,"<span id=\"navbut\">");
		if(report-1>=0){
			sprintf(buffer, "<a href=\"%s%s/%d\"><<</a>", baseName, sName, report-1);
			strcat(ret,buffer);
		}else{
			strcat(ret,"<<");
		}
		if(FileExists(next)){
			sprintf(buffer, "<a href=\"%s%s/%d\">>></a>", baseName, sName, report+1);
			strcat(ret,buffer);
		}else{
			strcat(ret,">>");
		}
		
	}
	strcat(ret, "</div>\n");
	return ret;
}

char* GetFileName(char* sName, int report){
	char ret[128];
	char nb[12];
	strcpy(ret, CLIENT_DATABASE);
	strcat(ret, sName);
	strcat(ret, "/");
	sprintf(nb, "report%.4d", report);
	strcat(ret, nb);
	return ret;
}

void HtmlAppend(char* str){
	int totalSize=strlen(str)+strlen(htmlContent)+1;
	if(totalSize>htmlContentSize){
		while(totalSize>htmlContentSize){
			htmlContentSize+=SIZE_STEP;
		}
		char* tmp=(char*)malloc(htmlContentSize);
		strcpy(tmp, htmlContent);
		strcat(tmp,str);
		free(htmlContent);
		htmlContent=tmp;
	}else{
		strcat(htmlContent,str);
	}
}

char* GetLine(char* str){
	int i;
	for(i=0;str[i]!='\n' && str[i]!='\000';++i);
	if(str[i]=='\000'){
		return NULL;
	}else{
		return str+i+1;
	}
}
